package ch5;

import util.Debug;
import util.Tools;

import java.util.Random;

/**
 * 演示使用Object.wait(long timeout) 实现超时等待控制
 */

public class TimeoutWaitExample {
    private static final Object lock = new Object();
    private static boolean ready = false;
    protected static final Random random = new Random();

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread() {
            @Override
            public void run() {
               for (;;) {
                   synchronized (lock) {
                       ready = random.nextInt(100) < 20 ? true : false;
                       if (ready) {
                           lock.notify();
                       }
                   }
                   // 使当前线程随机暂停一段时间
                   Tools.randomPause(500);
               }
            }
        };
        t.setDaemon(true);
        t.start();
        waiter(500);
    }

    public static void waiter(final long timeout) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException();
        }

        long start = System.currentTimeMillis();
        long waitTime;
        long now;
        synchronized (lock) {
            while (!ready) {
                now = System.currentTimeMillis();
                // 计算剩余等待时间
                waitTime = timeout - (now - start);
                Debug.info("Remaning time to wait: %sms", waitTime);
                if (waitTime <= 0) {
                    // 等待超时退出
                    break;
                }
                lock.wait(waitTime);
            }

            // 如果当前线程从wait中返回了，但是ready的值还是false，说明当前线程是因为等待超时而返回的，而不是因为被其他线程notify而返回的
            // 因此ready起到了这样的作用：即分辨当前线程是否是因为等待超时而返回
            if (ready) {
                // 执行目标动作
                guardedAction();
            } else {
                // 等待超时，保护条件未成立
                Debug.error("Wait timed out, unable to execution target action!");
            }
        }
    }

    private static void guardedAction() {
        Debug.info("Take some action");
    }
}
